package com.catering.until.file;

import com.catering.until.CommonUtils;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

@Service
@Slf4j
public class FileService {
	
	private final Path fileBeforeLocation; // 源文件在本地存储的地址
	private final Path fileLocation; // 压缩文件在本地存储的地址
	//源文件存储地址
	@Value("${file.upload-dir-before}")
	private String filedDir;
	//压缩后存储地址
	@Value("${file.upload-dir}")
	private String uploadDir;

    @Autowired
    public FileService(FileProperties fileProperties) {
        this.fileBeforeLocation = Paths.get(fileProperties.getUploadDirBefore()).toAbsolutePath().normalize();
        this.fileLocation = Paths.get(fileProperties.getUploadDir()).toAbsolutePath().normalize();
        try {
        	//没有目录则会创建
            Files.createDirectories(this.fileBeforeLocation);
            Files.createDirectories(this.fileLocation);
        } catch (Exception ex) {
        	ex.printStackTrace();
            throw new FileException("无法创建存储上载文件的目录.", ex);
        }
    }

    /**
     * 存储文件到系统
     *
     * @param file 文件
     * @return 文件名
     */
    public String storeFile(MultipartFile file,String type) {
        // 规范化文件名
    	String fileName = StringUtils.cleanPath(file.getOriginalFilename());
    	int lastIndexOf = fileName.lastIndexOf(".");
    	fileName = CommonUtils.getUuid()+fileName.substring(lastIndexOf);
        try {
            // 检查文件名是否包含无效字符
            if(fileName.contains("..")) {
                throw new FileException("文件名包含无效的路径序列 " + fileName);
            }
            Path beforeLocation = this.fileBeforeLocation.resolve(fileName);
            Path fileLocation = null;
            String newFileAdr = uploadDir;
            // 将文件复制到目标位置（用相同的名称替换现有文件）
            if("0".equals(type)) {
        		fileLocation = this.fileLocation.resolve(fileName);
        		newFileAdr = uploadDir;
            }else {
            	fileLocation = this.fileLocation.resolve(fileName);
            	newFileAdr = uploadDir;
            }
            //源文件存储
            Files.copy(file.getInputStream(), beforeLocation, StandardCopyOption.REPLACE_EXISTING);
            //压缩文件存储（实际上将文件存储两次，之后调用压缩文件方法进行压缩替换）
            Files.copy(file.getInputStream(), fileLocation, StandardCopyOption.REPLACE_EXISTING);
            compressPicForScale(filedDir+"/"+fileName, newFileAdr+"/"+fileName, 500, 0.8);
            return fileName;
        } catch (IOException ex) {
        	ex.printStackTrace();
            throw new FileException("无法存储文件 " + fileName + ". 请重试!", ex);
        }
    }

    /**
     * 加载文件
     * @param fileName 文件名
     * @return 文件
     */
    public Resource loadFileAsResource(String fileName) {
        try {
            Path filePath = this.fileBeforeLocation.resolve(fileName).normalize();
            Resource resource = new UrlResource(filePath.toUri());
            if(resource.exists()) {
                return resource;
            } else {
                throw new FileException("找不到文件 " + fileName);
            }
        } catch (MalformedURLException ex) {
            throw new FileException("找不到文件 " + fileName, ex);
        }
    }
    

	/**
     * @param srcPath     原图片地址
     * @param desPath     目标图片地址
     * @param desFileSize 指定图片大小，单位kb
     * @param accuracy    精度，递归压缩的比率，建议小于0.9
     * @return            图片压缩成功或者失败
     */
    public static boolean compressPicForScale(String srcPath, String desPath, long desFileSize, double accuracy) {
        try {
        	log.info("文件压缩开始!");
            File srcFile = new File(srcPath);
            //递归压缩，直到源文件文件大小小于desFileSize
            compressPicCycle(srcPath, desFileSize, accuracy);
            log.info("压缩后原文件:" + srcPath + ",大小:" + srcFile.length() / 1024 + "kb");
            File desFile = new File(desPath);
            //递归压缩，直到目标文件大小小于desFileSize
            compressPicCycle(desPath, desFileSize, accuracy);
            log.info("压缩后目标文件:" + desPath + ",大小" + desFile.length() / 1024 + "kb");
            log.info("文件压缩完成!");
        } catch (Exception e) {
            return false;
        }
        return true;
    }
    
	//递归压缩，直到目标文件大小小于desFileSize
	public static void compressPicCycle(String desPath, long desFileSize,double accuracy) throws IOException {
	    File imgFile = new File(desPath);
	    long fileSize = imgFile.length();
	    //判断大小，如果小于指定大小，不压缩；如果大于等于指定大小，压缩
	    if (fileSize <= desFileSize * 1024) {
	        return;
	    }
	    //计算宽高
	    BufferedImage bim = ImageIO.read(imgFile);
	    int imgWidth = bim.getWidth();
	    int imgHeight = bim.getHeight();
	    int desWidth = new BigDecimal(imgWidth).multiply(
	            new BigDecimal(accuracy)).intValue();
	    int desHeight = new BigDecimal(imgHeight).multiply(
	            new BigDecimal(accuracy)).intValue();
	    Thumbnails.of(desPath).size(desWidth, desHeight).outputQuality(accuracy).toFile(desPath);
	    //如果不满足要求，递归直至满足指定大小的要求
	    compressPicCycle(desPath, desFileSize, accuracy);
	}
}
